#include "slog2_context_data.h"
#include <iomanip>
#include <sstream>
#include <iostream>

#define SLOG2_DATA_MAX_SIZE 1024

Slog2ReturnValue Slog2ContextData::Initialize(Slog2Context *context)
{
    context_ = context;
    data_.reserve(SLOG2_DATA_MAX_SIZE);
    data_.clear();

    if (context == NULL) {
    	return Slog2ReturnValue::kReturnError;
    }

    return Slog2ReturnValue::kReturnOk;
}

Slog2ReturnValue Slog2ContextData::WriteStart(Slog2SeverityLevel severity)
{
    data_.clear();
    severity_ = severity;
    //std::string context_string = std::string("CTX ");
    //return concat(std::move(context_string));
    return Slog2ReturnValue::kReturnOk;
}

Slog2ReturnValue Slog2ContextData::WriteStart()
{
    data_.clear();
    return Slog2ReturnValue::kReturnOk;
}

Slog2ReturnValue Slog2ContextData::WriteFinish()
{
    uint8_t severity = SLOG2_INFO;
    const char *data = data_.c_str();
    slog2_buffer_t buffer_handle = NULL;

    if (context_ == NULL) {
        return Slog2ReturnValue::kReturnError;
    }
    /* 
    buffer_handle = context_->GetDefaultBuffer();
    if (buffer_handle == NULL) {
        return Slog2ReturnValue::kReturnError;
    }
    */
    switch (severity_)
    {
	case Slog2SeverityLevel::kShutdown:
			std::cout<< "SLOG2_SHUTDOWN:"<< SLOG2_SHUTDOWN <<std::endl;
            severity = SLOG2_SHUTDOWN;
            break;
        case Slog2SeverityLevel::kCritical:
			std::cout<< "SLOG2_CRITICAL"<<std::endl;
            severity = SLOG2_CRITICAL;
            break;
        case Slog2SeverityLevel::kError:
			std::cout<< "SLOG2_ERROR"<<std::endl;
            severity = SLOG2_ERROR;
            break;
        case Slog2SeverityLevel::kWarning:
			std::cout<< "SLOG2_WARNING"<<std::endl;
            severity = SLOG2_WARNING;
            break;
        case Slog2SeverityLevel::kNotice:
			std::cout<< "SLOG2_NOTICE"<<std::endl;
            severity = SLOG2_NOTICE;
            break;
        case Slog2SeverityLevel::kInfo:
			std::cout<< "SLOG2_INFO:"<< SLOG2_INFO <<std::endl;
            severity = SLOG2_INFO;
            break;
        case Slog2SeverityLevel::kDebug1:
			std::cout<< "SLOG2_DEBUG1"<<std::endl;
            severity = SLOG2_DEBUG1;
            break;
        case Slog2SeverityLevel::kDebug2:
			std::cout<< "SLOG2_DEBUG2: "<< SLOG2_DEBUG2 <<std::endl;
            severity = SLOG2_DEBUG2;
            break;
        default:
			std::cout<< "default SLOG2_INFO"<<std::endl;
            severity = SLOG2_INFO;
            break;
    }

    if(context_ && context_->IsConsoleEnabled()) {
         std::cout<< "severity:"<< severity <<" data: "<< data_ <<std::endl;
    }
    if(context_ && context_->IsRemoteEnabled()) {
        int ret = slog2c(buffer_handle, 0, severity, data);
        if (0 != ret) {
	    switch(errno){
	    	case EBUSY:
			std::cout<< "EBUSY"<<std::endl;
			break;
	    	case EFAULT:
			std::cout<< "EFAULT"<<std::endl;
			break;
	    	case EPERM:
			std::cout<< "EPERM"<<std::endl;
			break;
		default:
			std::cout<< "default "<<errno <<std::endl;
	    }
            return Slog2ReturnValue::kReturnError;
        }
    }

    return Slog2ReturnValue::kReturnOk;
}


Slog2ReturnValue Slog2ContextData::concat(std::string &&data)
{
    size_t needed_size = data.size() + 1;
    if ((data_.size() + needed_size) > data_.capacity())
        return Slog2ReturnValue::kReturnBufferFull;

    data_ += data;
    return Slog2ReturnValue::kReturnOk;
}

Slog2ReturnValue Slog2ContextData::Write(uint8_t value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(uint16_t value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(uint32_t value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(uint64_t value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(int8_t value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(int16_t value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(int32_t value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(int64_t value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(float value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(double value)
{
   std::string value_string = std::to_string(value);
   return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(const char *value)
{
    std::string value_string{value};
    return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(const std::string value)
{
   std::string value_string{value};
   return concat(std::move(value_string));
}


Slog2ReturnValue Slog2ContextData::Write(bool value)
{
    const std::string true_string = "true";
    const std::string false_string = "false";

    std::string value_string;
    if (value) {
        value_string = true_string;
    } else {
        value_string = false_string;
    }

    return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Write(std::vector<uint8_t> value)
{
    std::stringstream stringstream;
    // Store bytes in the stream in hexadecimal format and fill with '0'.
    stringstream << std::hex << std::setfill('0');

    for (uint8_t byte : value) {
        uint32_t byte_integer = static_cast<uint32_t>(byte);
        // Set width to two characters (0x00).
        stringstream << std::setw(2) << byte_integer;
    }

    std::string value_string = stringstream.str();
    return concat(std::move(value_string));
}

Slog2ReturnValue Slog2ContextData::Read(std::string &value)
{
    value = data_;

    return Slog2ReturnValue::kReturnOk;
}

